#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>

// 进程回收的回调函数
void recyle(int num)
{
    pid_t pid;
    // 信号里面用waitpid回收子进程的资源
    while ((pid = waitpid(-1, NULL, WNOHANG)) > 0)
    {
        printf("child died, pid = %d\n", pid);
    }
}

int main(int argc, const char *argv[])
{
    if (argc < 2)
    {
        printf("./a.out port");
        exit(1);
    }
    // 将字符串整型值
    int port = atoi(argv[1]);
    // 创建套接字
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    // 绑定本地IP和端口
    struct sockaddr_in serv;
    memset(&serv, 0, sizeof(serv));
    serv.sin_port = htons(port);
    serv.sin_addr.s_addr = INADDR_ANY;
    serv.sin_family = AF_INET;
    bind(lfd, (const struct sockaddr *)&serv, sizeof(serv));

    // 监听
    listen(lfd, 128);
    printf("start accept.......\n");

    // 等待接收连接请求
    struct sockaddr_in client;
    socklen_t len = sizeof(client);
    // cfd 通信套接字

    // 用信号回收子进程资源  这里不能用wait 不然的话会一直阻塞的不好
    struct sigaction act;
    act.sa_handler = recyle;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    sigaction(SIGCHLD, &act, NULL);

    while (1)
    {
        // 父进程接受连接请求
        int cfd = accept(lfd, (struct sockaddr *)&client, &len);
        if (cfd == -1 && errno == EINTR)
        {
            continue;
        }
        printf("connect sucessful\n");
        // 创建子进程
        pid_t pid = fork();
        if (pid == 0)
        {
            // 子进程关闭监听文件描述符
            close(lfd);
            // 通信
            char ip[64];
            while (1)
            {
                printf("client IP: %s, port: %d\n", inet_ntop(AF_INET, &client.sin_addr.s_addr, ip, sizeof(ip)), ntohs(client.sin_port));
                char buf[1024] = {0};
                int len = read(cfd, buf, sizeof(buf));
                if (len == -1)
                {
                    perror("read error");
                    exit(1);
                }
                else if (len == 0)
                {
                    printf("客户端断开了连接\n");
                    close(cfd);
                    break;
                }
                else
                {
                    printf("recv buf: %s\n", buf);
                    write(cfd, buf, len);
                }
            }
            return 0;
        }
        else if (pid > 0)
        {
            // 父进程关掉通信文件描述符
            close(cfd);
        }
    }

    return 0;
}
